home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 …ember: Reference Library / Dev.CD Dec 00 RL Disk 1.toast / pc / technical documentation / develop / develop issue 29 / develop issue 29 code / acgis in c / acgi.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-01-07  |  42.2 KB  |  1,930 lines

  1. #include <stdio.h>
  2. #include <string.h>
  3. #include <ctype.h>
  4.  
  5. #include "acgi.h"
  6. #include "www.h"
  7.  
  8. static char *gHTTPHeader =
  9.     "HTTP/1.0 200 OK\r\n"
  10.     "Server: WebSTAR/1.2.5\r\n"
  11.     "MIME-version: 1.0\r\n"
  12.     "Content-type: text/html\r\n\r\n";
  13.  
  14. static long gHTTPHeaderLen;
  15.  
  16. void ACGIInit(void);
  17. void ACGIEvent(EventRecord *theEvent);
  18. void ACGIPeriodicTask(void);
  19. void ACGIQuit(void);
  20.  
  21. static Boolean gDone = false;
  22. static unsigned long gThreads = 0;
  23.  
  24. static long gThreadSleep = 4;
  25. static long gIdleSleep = 0x7FFFFFFF;
  26. static long gWNEDelta = 8;
  27.  
  28. void main(void)
  29. {
  30.     EventRecord theEvent;
  31.     long sleep;
  32.     unsigned long nextWNE;
  33.  
  34.     ACGIInit(); // Initialize…
  35.  
  36.     // Main Event loop…
  37.  
  38.     while (!gDone || gThreads > 0) {
  39.         
  40.         if (gThreads > 0)
  41.             sleep = gThreadSleep;
  42.         else
  43.             sleep = gIdleSleep;
  44.             
  45.         if (WaitNextEvent(everyEvent, &theEvent, sleep, nil))
  46.             ACGIEvent(&theEvent);
  47.  
  48.         nextWNE = TickCount() + gWNEDelta;
  49.  
  50.         do {
  51.             YieldToAnyThread();
  52.         } while (TickCount() <= nextWNE);
  53.  
  54.         ACGIPeriodicTask();
  55.     }
  56.  
  57.     ACGIQuit(); // Shutdown…
  58. }
  59.  
  60. // -----------------------------------------------------
  61.  
  62. // For additional checks for Thread Manager on PowerPC...
  63.  
  64. #if GENERATINGCFM
  65. #include <codefragments.h>
  66. #endif
  67.  
  68. // These prototypes are defined in "www.h"
  69. //
  70. // char *WWWGetLogName(void);
  71. // void WWWGetHTMLPages(char **refused, char **tooBusy,
  72. //                char **noMemory, char **unexpectedError);
  73. // OSErr WWWInit(void);
  74.  
  75. // This prototype is defined in "acgi.h"
  76. // void ACGILog(char *msg);
  77.  
  78. void ACGIFatal(char *reason);
  79. void AEInstallHandlers(void);
  80.  
  81. static FILE *gLog = NULL; // The log file…
  82.  
  83. // Generic HTML error-response pages...
  84.  
  85. static Handle gHTMLNoMemory = nil;
  86. static Handle gHTMLUnexpectedError = nil;
  87. static Handle gHTMLRefused = nil;
  88. static Handle gHTMLTooBusy = nil;
  89.  
  90. #define kMaxMasters 20
  91.  
  92. static void ACGIInit(void)
  93. {
  94.     long i;
  95.     OSErr err;
  96.  
  97.     // [1]    Initialize the toolbox…
  98.  
  99.     MaxApplZone();
  100.     
  101.     for (i = 0; i < kMaxMasters; i++)
  102.             MoreMasters();
  103.     
  104.     InitGraf(&qd.thePort);
  105.     InitFonts();
  106.     InitWindows();
  107.     InitMenus();
  108.     TEInit();
  109.     InitDialogs(nil);
  110.     InitCursor();
  111.  
  112.     FlushEvents(everyEvent, 0);
  113.     
  114.     // [2]    Open the log file using the name returned by WWWGetLogName().
  115.     
  116.     gLog = fopen(WWWGetLogName(), "a");
  117.     
  118.     // [3]    See if we can run...
  119.     //
  120.     //    …we need Apple Events (which means System 7.0 or later)…
  121.     
  122.     {
  123.         SysEnvRec theSysEnv;
  124.  
  125.         err = SysEnvirons(curSysEnvVers, &theSysEnv);
  126.  
  127.         if (err != noErr || theSysEnv.systemVersion < 0x0700)
  128.             ACGIFatal("AppleEvents unavailable.");
  129.     }
  130.     
  131.     //    …and the Thread Manager (with additional checks for a
  132.     //        loaded Thread Library when compiling for PPC).
  133.  
  134.     {
  135.         long threadMgrAttr;
  136.         
  137.         err = Gestalt(gestaltThreadMgrAttr, &threadMgrAttr);
  138.  
  139.         if (
  140.             err != noErr ||
  141. #if GENERATINGCFM
  142.             threadMgrAttr & (1L << gestaltThreadsLibraryPresent) == 0 ||
  143.             (Ptr) NewThread == (Ptr) kUnresolvedSymbolAddress ||
  144. #endif
  145.             threadMgrAttr & (1L << gestaltThreadMgrPresent) == 0
  146.         ) ACGIFatal("Thread Manager unavailable");
  147.     }
  148.  
  149.     // [4]    Build the menu bar from an MBAR resource...
  150.     
  151.     {
  152.         #define    kMenuBarResID    1000
  153.  
  154.         #define    kAppleMenuResID    1000
  155.         #define    kFileMenuResID    1010
  156.  
  157.         Handle theBar;
  158.         MenuHandle appleMenu;
  159.     
  160.         theBar = GetNewMBar(kMenuBarResID);
  161.         if (theBar == nil) ACGIFatal("Could not load menu bar.");
  162.  
  163.         SetMenuBar(theBar);
  164.     
  165.         //    Append the "Apple Menu Items" to the Apple menu and then draw it…
  166.     
  167.         appleMenu = GetMenuHandle(kAppleMenuResID);
  168.         if (appleMenu == nil) ACGIFatal("Could not load Apple Menu.");
  169.     
  170.         AppendResMenu(appleMenu, 'DRVR');
  171.         DrawMenuBar();
  172.     }
  173.     
  174.     // [5]    Install the event handlers (SDOC handler will be threaded)…
  175.  
  176.     AEInstallHandlers();
  177.     
  178.     // [6]    Get the general HTML error response pages…
  179.     
  180.     gHTTPHeaderLen = strlen(gHTTPHeader);
  181.     
  182.     gHTMLRefused = NewHandle(gHTTPHeaderLen);
  183.     if (gHTMLRefused == nil) ACGIFatal("Ran out of memory making gHTMLRefused.");
  184.     BlockMoveData(gHTTPHeader, *gHTMLRefused, gHTTPHeaderLen);
  185.     
  186.     gHTMLTooBusy = NewHandle(gHTTPHeaderLen);
  187.     if (gHTMLTooBusy == nil) ACGIFatal("Ran out of memory making gHTMLTooBusy.");
  188.     BlockMoveData(gHTTPHeader, *gHTMLTooBusy, gHTTPHeaderLen);
  189.     
  190.     gHTMLNoMemory = NewHandle(gHTTPHeaderLen);
  191.     if (gHTMLNoMemory == nil) ACGIFatal("Ran out of memory making gHTMLNoMemory.");
  192.     BlockMoveData(gHTTPHeader, *gHTMLNoMemory, gHTTPHeaderLen);
  193.     
  194.     gHTMLUnexpectedError = NewHandle(gHTTPHeaderLen);
  195.     if (gHTMLUnexpectedError == nil) ACGIFatal("Ran out of memory making gHTMLUnexpectedError.");
  196.     BlockMoveData(gHTTPHeader, *gHTMLUnexpectedError, gHTTPHeaderLen);
  197.                                 
  198.     WWWGetHTMLPages(gHTMLRefused, gHTMLTooBusy,
  199.                                 gHTMLNoMemory, gHTMLUnexpectedError);
  200.     
  201.     // [7]    Give you a chance to set up your runtime environment
  202.     //        and get/set ACGI run parameters…
  203.  
  204.     {
  205.         err = WWWInit();
  206.  
  207.         if (err != noErr) {
  208.             char msg[64];
  209.             sprintf(msg, "WWWInit() failed. Error code = %d", err);
  210.             ACGIFatal(msg);
  211.         }
  212.     }
  213.     
  214.     // Write a startup message in the log…
  215.     
  216.     ACGILog("................................startup.");
  217. }
  218.  
  219. // -----------------------------------------------------
  220.  
  221. void ACGIFatal(char *reason)
  222. {
  223.     if (gLog != NULL) {
  224.         ACGILog(reason);
  225.         ACGILog("That was a fatal error..........shutdown.");
  226.     }
  227.     ExitToShell();
  228. }
  229.  
  230. void ACGILog(char *msg)
  231. {
  232.     DateTimeRec dt;
  233.     ThreadID theThread;
  234.  
  235.     if (gLog == NULL) return;
  236.  
  237.     GetTime(&dt);
  238.     GetCurrentThread(&theThread);
  239.     fprintf(gLog, "%4d/%02d/%02d\t%02d:%02d:%02d\t%010lu\t%s\n",
  240.                 dt.year, dt.month, dt.day,
  241.                 dt.hour, dt.minute, dt.second,
  242.                 theThread, msg);
  243.     fflush(gLog);
  244. }
  245.  
  246. // -----------------------------------------------------
  247.  
  248. void ACGIPeriodicTask(void)
  249. {
  250.     OSErr err = WWWPeriodicTask();
  251.  
  252.     if (err != noErr) {
  253.         char msg[64];
  254.  
  255.         sprintf(msg, "WWWPeriodicTask returned error: %d");
  256.         ACGILog(msg);
  257.  
  258.         if (err < 0) {
  259.             ACGILog("That was a fatal error...quitting.");
  260.             gDone = true;
  261.         }
  262.     }
  263. }
  264.  
  265. static void ACGIQuit(void)
  266. {
  267.     WWWQuit();
  268.  
  269.     ACGILog("................................shutdown.");
  270.  
  271.     if (gLog != NULL) fclose(gLog);
  272. }
  273.  
  274. // -----------------------------------------------------
  275.  
  276. void DoMenu(long menuResult);
  277.  
  278. static void ACGIEvent(EventRecord *theEvent)
  279. {
  280.     char msg[64];
  281.     OSErr err;
  282.  
  283.     switch (theEvent->what) {
  284.     
  285.         // No windows...so we can ignore activate and update events...
  286.  
  287.         case activateEvt:
  288.         case updateEvt:
  289.             break;
  290.  
  291.         // The only mouse-downs will be in the menu bar or in
  292.         // windows that don't belong to us...
  293.  
  294.         case mouseDown:
  295.             {
  296.                 WindowPtr theWindow;
  297.  
  298.                 switch (FindWindow(theEvent->where, &theWindow)) {
  299.                 
  300.                     case inMenuBar:
  301.                     
  302.                         DoMenu(MenuSelect(theEvent->where));
  303.                         break;
  304.                         
  305.                     case inSysWindow:
  306.                     
  307.                         SystemClick(theEvent, theWindow);
  308.                         break;
  309.                 }
  310.             }
  311.             break;
  312.  
  313.         // The only key-downs we'll get are menu-key equivalents...
  314.  
  315.         case keyDown:
  316.         case autoKey:
  317.             if ((theEvent->modifiers & cmdKey) != 0) {
  318.                 long menuResult = MenuKey(theEvent->message & charCodeMask);
  319.  
  320.                 if (HiWord(menuResult) != 0) DoMenu(menuResult);
  321.             }
  322.             break;
  323.  
  324.         // Suspend or Resume event...just set the cursor to an arrow...
  325.  
  326.         case osEvt:
  327.             if ((theEvent->message >> 24) & suspendResumeMessage)
  328.                 SetCursor(&qd.arrow);
  329.             break;
  330.  
  331.         // Always handle disk-inserted events...be a well-behaved app!
  332.  
  333.         case diskEvt:
  334.             if (HiWord(theEvent->message) != 0) {
  335.                     Point pt;
  336.                     
  337.                     SetPt(&pt, 120, 120);
  338.                     
  339.                     DILoad();
  340.                     err = DIBadMount(pt, theEvent->message);
  341.                     if (err != noErr) {
  342.                         sprintf(msg, "Main loop: DIBadMount() returned error: %d", err);
  343.                         ACGILog(msg);
  344.                     }
  345.                     DIUnload();
  346.             }
  347.             break;
  348.         
  349.         // Do AppleEvents like always...the threading is
  350.         // invisible to the main event loop...
  351.  
  352.         case kHighLevelEvent:
  353.             err = AEProcessAppleEvent(theEvent);
  354.             if (err != noErr) {
  355.                 sprintf(msg, "Main loop: AEProcessAppleEvent() returned error: %d", err);
  356.                 ACGILog(msg);
  357.             }
  358.             break;
  359.     }
  360.  
  361.     return;
  362. }
  363.  
  364. // We do the "usual" menu stuff...except we don't do
  365. // the "About..." box thing...
  366. //
  367. // We do run desk accessories though...
  368. //
  369.  
  370. #define kAboutItem 1
  371.  
  372. static void DoMenu(long menuResult)
  373. {
  374.     short theMenu = HiWord(menuResult);
  375.     short theItem = LoWord(menuResult);
  376.     
  377.     switch (theMenu) {
  378.     
  379.         case kAppleMenuResID:
  380.         
  381.             if (theItem != kAboutItem) {
  382.                 MenuHandle appleMenu = GetMenuHandle(kAppleMenuResID);
  383.  
  384.                 if (appleMenu != nil) {
  385.                     GrafPtr savePort;
  386.                     Str255 daName;
  387.                     
  388.                     GetPort(&savePort);
  389.                     SetCursor(&qd.arrow);
  390.                     GetMenuItemText(appleMenu, theItem, daName);
  391.                     OpenDeskAcc(daName);
  392.                     SetPort(savePort);
  393.                 }
  394.                 else {
  395.                     ACGILog("DoMenu: couldn't get Apple Menu handle.");
  396.                 }
  397.             }
  398.             break;
  399.             
  400.         case kFileMenuResID:
  401.         
  402.             gDone = true;
  403.             break;
  404.             
  405.      }
  406.     
  407.     // Don't forget to unhilight the menu bar when we're done...
  408.      
  409.     HiliteMenu(0);
  410. }
  411.  
  412. // -----------------------------------------------------
  413.  
  414. #define kQuitCoreEvent    1
  415. #define kOtherCoreEvent    0
  416.  
  417. pascal OSErr HandleAECore(AppleEvent *event, AppleEvent *reply, long refCon);
  418.  
  419. static pascal OSErr HandleAECore(AppleEvent *event, AppleEvent *reply, long refCon)
  420. {
  421.     if (refCon == kQuitCoreEvent) gDone = true;
  422.  
  423.     return (noErr);
  424. }
  425.  
  426. // -----------------------------------------------------
  427.  
  428. #define kWebSTAREventClass    'WWWΩ'
  429. #define kSDOCEvent    'sdoc'
  430.  
  431. // Prototype for our SDOC handler routine.
  432.  
  433. pascal OSErr HandleSDOC(AppleEvent *event, AppleEvent *reply, long refCon);
  434.  
  435. // -----------------------------------------------------
  436.  
  437. static void AEInstallHandlers(void)
  438. {
  439.     OSErr err;
  440.     
  441.     // First, allocate a UPP for the core event handler...
  442.  
  443.     AEEventHandlerUPP eventUPP = NewAEEventHandlerProc(HandleAECore);
  444.     
  445.     if (eventUPP == nil) ACGIFatal("Could not allocate UPP for core AE handler.");
  446.  
  447.     // [1] open app...
  448.  
  449.     err = AEInstallEventHandler(kCoreEventClass, kAEOpenApplication,
  450.                                     eventUPP, kOtherCoreEvent, false);
  451.  
  452.     if (err != noErr) ACGIFatal("Could not install 'open app' handler.");
  453.     
  454.     // [2] open doc...
  455.  
  456.     err = AEInstallEventHandler(kCoreEventClass, kAEOpenDocuments,
  457.                                     eventUPP, kOtherCoreEvent, false);
  458.  
  459.     if (err != noErr) ACGIFatal("Could not install 'open doc' handler.");
  460.     
  461.     // [3] print doc...
  462.  
  463.     err = AEInstallEventHandler(kCoreEventClass, kAEPrintDocuments,
  464.                                     eventUPP, kOtherCoreEvent, false);
  465.  
  466.     if (err != noErr) ACGIFatal("Could not install 'print doc' handler.");
  467.     
  468.     // [4] quit app...
  469.  
  470.     err = AEInstallEventHandler(kCoreEventClass, kAEQuitApplication,
  471.                                     eventUPP, kQuitCoreEvent, false);
  472.     
  473.     if (err != noErr) ACGIFatal("Could not install 'quit app' handler.");
  474.     
  475.     // [5] sdoc event...
  476.     
  477.     eventUPP = NewAEEventHandlerProc(HandleSDOC);
  478.     
  479.     if (eventUPP == nil) ACGIFatal("Could not allocate UPP for SDOC handler.");
  480.  
  481.     err = AEInstallEventHandler(kWebSTAREventClass, kSDOCEvent,
  482.                                 eventUPP, 0, false);
  483.  
  484.     if (err != noErr)
  485.         ACGIFatal("Could not install 'search document' handler.");
  486. }
  487.  
  488. // -----------------------------------------------------
  489.  
  490. // Maximum number of threads...
  491.  
  492. static unsigned long gMaxThreads = 10;
  493.  
  494. // Are we refusing connections...
  495.  
  496. static Boolean gRefusing = false;
  497.  
  498. // Thread stack-size and creation options...
  499.  
  500. static long gThreadStackSize = 0;
  501. static ThreadOptions gThreadOptions = kCreateIfNeeded | kFPUNotNeeded;
  502.  
  503. // Private data structure used to pass AppleEvent information into the thread...
  504.  
  505. typedef struct AEParams {
  506.     AppleEvent event;
  507.     AppleEvent reply;
  508. } AEParams;
  509.  
  510. void SDOCThread(void *threadParam);
  511.  
  512. OSErr ACGIReturnHandle(AppleEvent *reply, Handle h);
  513.  
  514. // This handler is called when we get a WebSTAR request...we need to
  515. // create a thread to handle the request and get out...
  516.  
  517. pascal OSErr HandleSDOC(AppleEvent *event, AppleEvent *reply, long refCon)
  518. {
  519.     AEParams** params;
  520.     ThreadID newThreadID;
  521.  
  522.     // [1]    Are too many threads already running?
  523.     
  524.     if (gThreads >= gMaxThreads)
  525.         return (ACGIReturnHandle(reply, gHTMLTooBusy));
  526.     
  527.     // [2]    Are we refusing requests?
  528.     
  529.     if (gDone || gRefusing)
  530.         return (ACGIReturnHandle(reply, gHTMLRefused));
  531.  
  532.     // [3] Okay to run, fork a new SDOC thread...
  533.  
  534.     gThreads++;
  535.  
  536.     params = (AEParams**) NewHandle(sizeof(AEParams));
  537.  
  538.     if (params == nil) return (errAEEventNotHandled);
  539.     
  540.     // [4] Copy the event information so we can pass it
  541.     //     in to the new thread for processing.
  542.  
  543.     (*params)->event = *event;
  544.     (*params)->reply = *reply;
  545.         
  546.     // [5] Make a new thread..bail out (and dispose of memory) if we fail...
  547.  
  548.     if (NewThread(kCooperativeThread,
  549.                     (ThreadEntryProcPtr) SDOCThread,
  550.                     (void*) params,
  551.                     gThreadStackSize,
  552.                     gThreadOptions,
  553.                     nil,
  554.                     &newThreadID) != noErr) {
  555.         
  556.         DisposeHandle((Handle) params);
  557.         return (errAEEventNotHandled);
  558.     }
  559.  
  560.     // [6] Suspend the current event so we can accept new events.
  561.  
  562.     return (AESuspendTheCurrentEvent(event));
  563. }
  564.  
  565. // -----------------------------------------------------
  566.  
  567. #define kPathArgsKeyword        keyDirectObject
  568. #define kSearchArgsKeyword        'kfor'
  569. #define kPostArgsKeyword        'post'
  570.  
  571. #define kUsernameKeyword        'user'
  572. #define kPasswordKeyword        'pass'
  573. #define kFromUserKeyword        'frmu'
  574. #define kClientAddressKeyword    'addr'
  575. #define kServerAddressKeyword    'svnm'
  576. #define kServerIPPortKeyword    'svpt'
  577. #define kScriptNameKeyword        'scnm'
  578. #define kContentTypeKeyword        'ctyp'
  579. #define kReferrerKeyword        'refr'
  580. #define kUserAgentKeyword        'Agnt'
  581.  
  582. #define kActionKeyword            'Kact'
  583. #define kActionPathKeyword        'Kapt'
  584. #define kMethodKeyword            'meth'
  585. #define kClientIPKeyword        'Kcip'
  586. #define kFullRequestKeyword        'Kfrq'
  587. #define kConnectionIDKeyword    'Kcid'
  588.  
  589. // -----------------------------------------------------
  590.  
  591. static Size ACGIParamSize(AppleEvent *event);
  592. OSErr ACGICopyArgs(AppleEvent *event, WWWRequest r);
  593. OSErr ACGIURLDecode(char *data, long *n, Handle *names, Handle *values);
  594.  
  595. static void SDOCThread(void *threadParam)
  596. {
  597.     WWWRequestRecord request;
  598.     Size spaceNeeded;
  599.     Size responseSize;
  600.     
  601.     OSErr err;
  602.     
  603.     // [1]    Copy event, reply to local storage.
  604.         
  605.     AEParams** params = (AEParams**) threadParam;
  606.  
  607.     AppleEvent event = (*params)->event;
  608.     AppleEvent reply = (*params)->reply;
  609.  
  610.     DisposeHandle((Handle) params);
  611.  
  612.     // [2]    Initialize 'request' structure...
  613.     
  614.     memset(&request, 0, sizeof(request));
  615.     
  616.     // [3]    Allocate storage for params/args.
  617.     
  618.     spaceNeeded = ACGIParamSize(&event);
  619.     request.storage = NewHandleClear(spaceNeeded);
  620.     if (request.storage == nil) {
  621.         char msg[128];
  622.  
  623.         sprintf(msg, "SDOCThread: no storage memory: %lu bytes.", spaceNeeded);
  624.         ACGILog(msg);
  625.         err = ACGIReturnHandle(&reply, gHTMLNoMemory);
  626.         gDone = true;
  627.         
  628.         goto Done;
  629.     }
  630.     
  631.     HLockHi(request.storage);
  632.  
  633.     // [4]    Copy params/args into position...
  634.     
  635.     err = ACGICopyArgs(&event, &request);
  636.     if (err != noErr) goto Done;
  637.     
  638.     // [5]    Decode URL-encoded SEARCH and POST arguments...
  639.     
  640.     if (strlen(*request.storage + (long) request.searchArgs) > 0) {
  641.  
  642.         err = ACGIURLDecode(
  643.                     *request.storage + (long) request.searchArgs,
  644.                     &request.searchNum,
  645.                     &request.searchNames,
  646.                     &request.searchValues);
  647.  
  648.         if (err != noErr) goto Done;
  649.     }
  650.  
  651.     if (strlen(*request.storage + (long) request.postArgs) > 0) {
  652.  
  653.         err = ACGIURLDecode(
  654.                     *request.storage + (long) request.postArgs,
  655.                     &request.postNum,
  656.                     &request.postNames,
  657.                     &request.postValues);
  658.  
  659.         if (err != noErr) goto Done;
  660.     }
  661.     
  662.     HUnlock(request.storage);
  663.     
  664.     // [6]    Allocate HTML response...
  665.     
  666.     request.response = NewHandleClear(gHTTPHeaderLen);
  667.  
  668.     if (request.response == nil) {
  669.         gDone = true;
  670.         err = ACGIReturnHandle(&reply, gHTMLNoMemory);
  671.         
  672.         goto Done;
  673.     }
  674.     
  675.     BlockMoveData(gHTTPHeader, *request.response, gHTTPHeaderLen);
  676.  
  677.     // [7]    Call the custom processor...
  678.  
  679.     err = WWWProcess(&request);
  680.     
  681.     // [8] Put the response into the reply and resume the Apple event...
  682.     
  683. Done:
  684.  
  685.     if (request.storage != nil) DisposeHandle(request.storage);
  686.     if (request.searchNames != nil)
  687.         DisposeHandle(request.searchNames);
  688.     if (request.searchValues != nil)
  689.         DisposeHandle(request.searchValues);
  690.     if (request.postNames != nil)
  691.         DisposeHandle(request.postNames);
  692.     if (request.postValues != nil)
  693.         DisposeHandle(request.postValues);
  694.     
  695.     responseSize = GetHandleSize(request.response);
  696.     
  697.     if (err == noErr && request.response != nil
  698.                                                     && responseSize > gHTTPHeaderLen) {
  699.         err = ACGIReturnHandle(&reply, request.response);
  700.     }
  701.     else { switch (err) {
  702.             case errWWWNoMemory:
  703.                 err = ACGIReturnHandle(&reply, gHTMLNoMemory);
  704.                 break;
  705.             case errWWWRefused:
  706.                 err = ACGIReturnHandle(&reply, gHTMLRefused);
  707.                 break;
  708.             case errWWWTooBusy:
  709.                 err = ACGIReturnHandle(&reply, gHTMLTooBusy);
  710.                 break;
  711.             case errWWWUnexpected:
  712.                 err = ACGIReturnHandle(&reply, gHTMLUnexpectedError);
  713.                 break;
  714.             default:
  715.                 err = ACGIReturnHandle(&reply, gHTMLUnexpectedError);
  716.                 break; }    
  717.     }
  718.  
  719.     if (request.response != nil) DisposeHandle(request.response);
  720.     
  721.     // [9] Put error code into AppleEvent (if needed)...
  722.  
  723.     if (err != noErr) {
  724.         long errorResult = err; // Must be long integer.
  725.         
  726.         AEPutParamPtr(&reply, keyErrorNumber,
  727.             typeLongInteger, &errorResult, sizeof(long));
  728.     }
  729.     
  730.     // [10] Resume the event, decrement running thread count, write to the log…
  731.  
  732.     AEResumeTheCurrentEvent(&event, &reply,
  733.                             (AEEventHandlerUPP) kAENoDispatch, 0);
  734.     
  735.     gThreads--;
  736.  
  737.     ACGILog("Done.");
  738.  
  739.     return;
  740. }
  741.  
  742. // -----------------------------------------------------
  743.  
  744. #define ACGI_GET_SIZE(what)    \
  745.     err = AESizeOfParam(event, (what), &typeCode, &dataSize); \
  746.     if (err == noErr) spaceNeeded += dataSize + 1; \
  747.     if (err == errAEDescNotFound) spaceNeeded++;
  748.  
  749. static Size ACGIParamSize(AppleEvent *event)
  750. {
  751.     Size spaceNeeded = 0;
  752.     Size dataSize;
  753.     DescType typeCode;
  754.     OSErr err;
  755.     
  756.     ACGI_GET_SIZE(kPathArgsKeyword);
  757.     ACGI_GET_SIZE(kSearchArgsKeyword);
  758.  
  759.     ACGI_GET_SIZE(kUsernameKeyword);
  760.     ACGI_GET_SIZE(kPasswordKeyword);
  761.     ACGI_GET_SIZE(kFromUserKeyword);
  762.     ACGI_GET_SIZE(kClientAddressKeyword);
  763.     ACGI_GET_SIZE(kServerAddressKeyword);
  764.     ACGI_GET_SIZE(kServerIPPortKeyword);
  765.     ACGI_GET_SIZE(kScriptNameKeyword);
  766.     ACGI_GET_SIZE(kContentTypeKeyword);
  767.     ACGI_GET_SIZE(kReferrerKeyword);
  768.     ACGI_GET_SIZE(kUserAgentKeyword);
  769.  
  770.     ACGI_GET_SIZE(kActionKeyword);
  771.     ACGI_GET_SIZE(kActionPathKeyword);
  772.     ACGI_GET_SIZE(kMethodKeyword);
  773.     ACGI_GET_SIZE(kClientIPKeyword);
  774.     ACGI_GET_SIZE(kFullRequestKeyword);
  775.  
  776.     ACGI_GET_SIZE(kPostArgsKeyword);
  777.  
  778.     spaceNeeded += 11;    // For ConnectionID string;
  779.     
  780.     return (spaceNeeded);
  781. }
  782.  
  783. static OSErr ACGIOneArg(AppleEvent *event, AEKeyword what, char **where, char *pStart, char **p, char *pEnd)
  784. {
  785.     Size dataSize;
  786.     DescType typeCode;
  787.     
  788.     OSErr err = AEGetParamPtr(event, what, typeChar, &typeCode, *p, pEnd - *p, &dataSize);
  789.  
  790.     if (err != noErr && err != errAEDescNotFound) return (err);
  791.  
  792.     *where = (char *) (*p - pStart);
  793.     
  794.     if (err != errAEDescNotFound) *p += dataSize;
  795.     
  796.     (*p)++;
  797.     
  798.     return (noErr);
  799. }
  800.  
  801. #define ACGI_GET_ARG(what, where) \
  802.         err = ACGIOneArg(event, (what), &where, pStart, &p, pEnd); \
  803.         if (err != noErr) return (err);
  804.     
  805. OSErr ACGICopyArgs(AppleEvent *event, WWWRequest r)
  806. {
  807.     char *pStart, *p, *pEnd;
  808.     OSErr err;
  809.     
  810.     pStart = *r->storage;
  811.     p = pStart;
  812.     pEnd = pStart + GetHandleSize(r->storage) - 1;
  813.         
  814.     ACGI_GET_ARG(kPathArgsKeyword, r->pathArgs);
  815.     ACGI_GET_ARG(kSearchArgsKeyword, r->searchArgs);
  816.  
  817.     ACGI_GET_ARG(kUsernameKeyword, r->username);
  818.     ACGI_GET_ARG(kPasswordKeyword, r->password);
  819.     ACGI_GET_ARG(kFromUserKeyword, r->fromUser);
  820.     ACGI_GET_ARG(kClientAddressKeyword, r->clientAddress);
  821.     ACGI_GET_ARG(kServerAddressKeyword, r->serverName);
  822.     ACGI_GET_ARG(kServerIPPortKeyword, r->serverPort);
  823.     ACGI_GET_ARG(kScriptNameKeyword, r->scriptName);
  824.     ACGI_GET_ARG(kContentTypeKeyword, r->contentType);
  825.     ACGI_GET_ARG(kReferrerKeyword, r->referer);
  826.     ACGI_GET_ARG(kUserAgentKeyword, r->userAgent);
  827.  
  828.     ACGI_GET_ARG(kActionKeyword, r->action);
  829.     ACGI_GET_ARG(kActionPathKeyword, r->actionPath);
  830.     ACGI_GET_ARG(kMethodKeyword, r->method);
  831.     ACGI_GET_ARG(kClientIPKeyword, r->clientIP);
  832.     ACGI_GET_ARG(kFullRequestKeyword, r->fullRequest);
  833.     ACGI_GET_ARG(kConnectionIDKeyword, r->connectionID);
  834.     ACGI_GET_ARG(kPostArgsKeyword, r->postArgs);
  835.     
  836.     r->event = event;
  837.     
  838.     return (noErr);
  839. }
  840.  
  841. // -----------------------------------------------------
  842.  
  843. Boolean ACGIDecodeCStr(char *s);
  844.  
  845. OSErr ACGIURLDecode(char *data, long *n, Handle *names, Handle *values)
  846. {
  847.     char *pStart, *p, *pEnd, **nameList, **valueList;
  848.     unsigned long i = 1;
  849.     char lastDelim = '&';
  850.     
  851.     pStart = data;
  852.     pEnd = pStart + strlen(data);
  853.     p = pStart;
  854.     
  855.     // First, count up the number of arguments...
  856.     
  857.     while (*p != 0) {
  858.         if (*p == '&') i++;
  859.         p++;
  860.     }
  861.     
  862.     // Try to allocate memory to hold them...
  863.     
  864.     *names = NewHandleClear(i * sizeof(char *));
  865.     if (*names == nil) {
  866.         ACGILog("ACGIURLDecode: no memory for POST names.");
  867.         return(memFullErr);
  868.     }
  869.     
  870.     *values = NewHandleClear(i * sizeof(char *));
  871.     if (*names == nil) {
  872.         ACGILog("ACGIURLDecode: no memory for POST values.");
  873.         return(memFullErr);
  874.     }
  875.     
  876.     // Make some nice clean array pointers out of the handles.
  877.  
  878.     nameList = ((char **) **names);
  879.     valueList = ((char **) **values);
  880.     
  881.     // Scan for name=value pairs and record their positions.
  882.  
  883.     *n = i;
  884.     
  885.     p = pStart;
  886.     i = 0;
  887.     
  888.     do {
  889.         while (*p != '&' && *p != '=' && *p != 0) p++;
  890.         
  891.         // found a name...record in nameList...
  892.         
  893.         if (lastDelim == '&' && *p == '=') {
  894.             nameList[i] = (char *) (pStart - data); *p++ = 0;
  895.  
  896.             if (!ACGIDecodeCStr(pStart)) return (2);
  897.             
  898.             pStart = p;
  899.  
  900.             lastDelim = '=';
  901.         }
  902.         
  903.         // found a value...record in valueList...
  904.         
  905.         else if (lastDelim == '=' && (*p == '&' || *p == 0)) {
  906.             valueList[i++] = (char *) (pStart - data); *p++ = 0;
  907.             
  908.             if (!ACGIDecodeCStr(pStart)) return (2);
  909.  
  910.             pStart = p;
  911.  
  912.             lastDelim = '&';
  913.         }
  914.         
  915.         // error in the post argument string...fatal...
  916.         
  917.         else {
  918.             ACGILog("ACGIURLDecode: failed parsing post arguments.");
  919.             return (errWWWUnexpected);
  920.         }
  921.  
  922.     } while (i < *n && p < pEnd);
  923.     
  924.     return (noErr);
  925. }
  926.  
  927. static Boolean ACGIDecodeCStr(char *s)
  928. {
  929.     long n = strlen(s);
  930.     long i, j;
  931.     char c1, c2;
  932.     Handle hXLAT = Get1Resource('xlat', 1000);
  933.  
  934.     for (i = j = 0; i < n; i++) {
  935.  
  936.         // '+' signs become blanks...
  937.  
  938.         if (s[i] == '+')
  939.             s[j++] = ' ';
  940.  
  941.         // '%' signals start of 3-character hex sequence...
  942.  
  943.         else if (s[i] == '%') {
  944.             if (i + 2 >= n) return (false);
  945.             
  946.             c1 = toupper(s[i + 1]);
  947.             c2 = toupper(s[i + 2]);
  948.             
  949.             i += 2;
  950.             
  951.             if (!isxdigit(c1)) return (false);
  952.             if (!isxdigit(c2)) return (false);
  953.  
  954.             if (isdigit(c1))
  955.                 c1 -= '0';
  956.             else
  957.                 c1 = (c1 - 'A') + 10;
  958.             
  959.             if (isdigit(c2))
  960.                 c2 -= '0';
  961.             else
  962.                 c2 = (c2 - 'A') + 10;
  963.             
  964.             s[j++] = (c1 << 4) | c2;
  965.         }
  966.         
  967.         // All else gets passed through as long as it's
  968.         // not a control character (0x00 - 0x1F and 0x7F).
  969.         // The only exceptions are carriage returns (0x0D)
  970.         // and line-feeds (0x0A). They all get mapped to
  971.         // carriage returns except when they appear as
  972.         // carriage return/line feed pairs. They get
  973.         // replaced by single carriage returns.
  974.         
  975.         else {
  976.             if (!iscntrl(s[i]) || s[i] == 0x0D || s[i] == 0x0A) {
  977.                 if (s[i] < ' ' && s[i + 1] < ' ') {
  978.                     s[j++] = 0x0D; i++;
  979.                 }
  980.                 else if (s[i] == 0x0A)
  981.                     s[j++] = 0x0D;
  982.                 else
  983.                     s[j++] = s[i];
  984.             }
  985.         }
  986.     }
  987.     
  988.     // Terminate the string...
  989.     
  990.     s[j] = 0;
  991.             
  992.     // Now transliterate it from ISO Latin-1 to Macintosh Roman...
  993.     
  994.     if (hXLAT != nil) {
  995.         for (i = 0; i < strlen(s); i++) {
  996.             s[i] = ((char *) *hXLAT)[(unsigned char) s[i]];
  997.         }
  998.     }
  999.     
  1000.     return (true);
  1001. }
  1002.  
  1003. // -----------------------------------------------------
  1004.  
  1005. // [1]    Shut down the ACGI as soon as all current threads are finished.
  1006.  
  1007. void ACGIShutdown(void)
  1008. {
  1009.     gDone = true;
  1010. }
  1011.  
  1012. // [2]    Is the ACGI shutting down?
  1013.  
  1014. Boolean ACGIIsShuttingdown(void)
  1015. {
  1016.     return (gDone);
  1017. }
  1018.  
  1019. // [3]    Toggle acceptance/rejection of requests.
  1020.  
  1021. Boolean ACGIRefuse(Boolean refuse)
  1022. {
  1023.     Boolean oldRefuse = gRefusing;
  1024.  
  1025.     gRefusing = refuse;
  1026.  
  1027.     return (oldRefuse);
  1028. }
  1029.  
  1030. // [4]    Get number of active threads.
  1031.  
  1032. unsigned long ACGIGetRunningThreads(void)
  1033. {
  1034.     return (gThreads);
  1035. }
  1036.  
  1037. // [5]    Get maximum number of threads able to run at once.
  1038.  
  1039. unsigned long ACGIGetMaxThreads(void)
  1040. {
  1041.     return (gMaxThreads);
  1042. }
  1043.  
  1044. // [6]    Set the maximum number of allowed threads.
  1045.  
  1046. void ACGISetMaxThreads(unsigned long newThreads)
  1047. {
  1048.     gMaxThreads = newThreads;
  1049. }
  1050.  
  1051. // [7]    Get current sleep settings.
  1052.  
  1053. void ACGIGetSleeps(long *whenThreads, long *whenIdle)
  1054. {
  1055.     *whenThreads = gThreadSleep;
  1056.     *whenIdle = gIdleSleep;
  1057. }
  1058.  
  1059. // [8]    Set new sleep settings.
  1060.  
  1061. void ACGISetSleeps(long whenThreads, long whenIdle)
  1062. {
  1063.     gThreadSleep = whenThreads;
  1064.     gIdleSleep = whenIdle;
  1065. }
  1066.  
  1067. // [9]    Get current time between calls to WaitNextEvent().
  1068.  
  1069. long ACGIGetWNEDelta(void)
  1070. {
  1071.     return (gWNEDelta);
  1072. }
  1073.  
  1074. // [10]    Set current time between calls to WaitNextEvent().
  1075.  
  1076. void ACGISetWNEDelta(long newDelta)
  1077. {
  1078.     gWNEDelta = newDelta;
  1079. }
  1080.  
  1081. // [11]    Get current thread stack size.
  1082.  
  1083. void ACGIGetThreadParams(Size *stack, ThreadOptions *options)
  1084. {
  1085.     *stack = gThreadStackSize;
  1086.     *options = gThreadOptions;
  1087.     return;
  1088. }
  1089.  
  1090. // [12]    Set thread stack size.
  1091.  
  1092. void ACGISetThreadParams(Size stack, ThreadOptions options)
  1093. {
  1094.     gThreadStackSize = stack;
  1095.     gThreadOptions = options;
  1096. }
  1097.  
  1098. // [13]    Get a pointer to the standard HTTP header.
  1099.  
  1100. const char *ACGIGetHTTPHeader(void)
  1101. {
  1102.     return (gHTTPHeader);
  1103. }
  1104.  
  1105. // [14] Put a handle into a reply.
  1106.  
  1107. OSErr ACGIReturnHandle(AppleEvent *reply, Handle h)
  1108. {
  1109.     OSErr err;
  1110.     
  1111.     if (h == nil) return (noErr);
  1112.  
  1113.     HLockHi(h);
  1114.     err = AEPutParamPtr(reply, keyDirectObject, typeChar,
  1115.                 *h, GetHandleSize(h));
  1116.     HUnlock(h);
  1117.     
  1118.     return (err);
  1119. }
  1120.  
  1121. // -----------------------------------------------------
  1122.  
  1123. // [1] Lock down your 'request' parameters before accessing contents.
  1124.  
  1125. Boolean HTTPLockParams(WWWRequest r)
  1126. {
  1127.     long i;
  1128.     char **nameList;
  1129.     char **valueList;
  1130.     
  1131.     if (r->isLocked) return (true);
  1132.  
  1133.     HLockHi(r->storage);
  1134.     
  1135.     r->isLocked = true;
  1136.     
  1137.     // Editor's Note: the following code is a bit obscure, so here's an explanation,
  1138.     // hopefully it will save you some tim if you're trying to figure it out, like I did.
  1139.     // Initially, each of these "pointer" fields contains an offset into the "storage" string. 
  1140.     // Below we add to each one the pointer to the start of "storage." The result is
  1141.     // that when we're done, each field contains the correct pointer into "storage." 
  1142.     // The casting is necessary to make it compile, but does not reflect reality: the
  1143.     // operand on the left is the offset, that on the right is the pointer, even though
  1144.     // we're telling the compiler exactly the opposite. Clever trick, but it sure is hard to
  1145.     // read...
  1146.     // dkj
  1147.     r->pathArgs += (unsigned long)*r->storage;
  1148.     r->username += (unsigned long)*r->storage;
  1149.     r->password += (unsigned long)*r->storage;
  1150.     r->fromUser += (unsigned long)*r->storage;
  1151.     r->clientAddress += (unsigned long)*r->storage;
  1152.     r->serverName += (unsigned long)*r->storage;
  1153.     r->serverPort += (unsigned long)*r->storage;
  1154.     r->scriptName += (unsigned long)*r->storage;
  1155.     r->contentType += (unsigned long)*r->storage;
  1156.     r->referer += (unsigned long)*r->storage;
  1157.     r->userAgent += (unsigned long)*r->storage;
  1158.     r->action += (unsigned long)*r->storage;
  1159.     r->actionPath += (unsigned long)*r->storage;
  1160.     r->method += (unsigned long)*r->storage;
  1161.     r->clientIP += (unsigned long)*r->storage;
  1162.     r->fullRequest += (unsigned long)*r->storage;
  1163.     r->connectionID += (unsigned long)*r->storage;
  1164.  
  1165.     r->searchArgs += (unsigned long)*r->storage;
  1166.  
  1167.     if (r->searchNum > 0) {
  1168.         HLockHi(r->searchNames);
  1169.         HLockHi(r->searchValues);
  1170.  
  1171.         nameList = ((char **) *(r->searchNames));
  1172.         valueList = ((char **) *(r->searchValues));
  1173.  
  1174.         for (i = 0; i < r->searchNum; i++) {
  1175.             nameList[i] += (unsigned long)r->searchArgs;
  1176.             valueList[i] += (unsigned long)r->searchArgs;
  1177.         }
  1178.     }
  1179.  
  1180.     r->postArgs += (unsigned long)*r->storage;
  1181.  
  1182.     if (r->postNum > 0) {
  1183.         HLockHi(r->postNames);
  1184.         HLockHi(r->postValues);
  1185.  
  1186.         nameList = ((char **) *(r->postNames));
  1187.         valueList = ((char **) *(r->postValues));
  1188.  
  1189.         for (i = 0; i < r->postNum; i++) {
  1190.             nameList[i] += (unsigned long)r->postArgs;
  1191.             valueList[i] += (unsigned long)r->postArgs;
  1192.         }
  1193.     }
  1194.     
  1195.     return (true);
  1196. }
  1197.  
  1198. // [2] Unlock your request parameters.
  1199.  
  1200. void HTTPUnlockParams(WWWRequest r)
  1201. {
  1202.     long i;
  1203.     char **nameList;
  1204.     char **valueList;
  1205.     
  1206.     if (!r->isLocked) return;
  1207.     
  1208.     r->isLocked = false;
  1209.     
  1210.     if (r->searchNum > 0) {
  1211.         nameList = ((char **) *(r->searchNames));
  1212.         valueList = ((char **) *(r->searchValues));
  1213.     
  1214.         for (i = 0; i < r->searchNum; i++) {
  1215.             nameList[i] -= (unsigned long)r->searchArgs;
  1216.             valueList[i] -= (unsigned long)r->searchArgs;
  1217.         }
  1218.         
  1219.         HUnlock(r->searchNames);
  1220.         HUnlock(r->searchValues);
  1221.     }
  1222.  
  1223.     if (r->postNum > 0) {
  1224.         nameList = ((char **) *(r->postNames));
  1225.         valueList = ((char **) *(r->postValues));
  1226.     
  1227.         for (i = 0; i < r->postNum; i++) {
  1228.             nameList[i] -= (unsigned long)r->postArgs;
  1229.             valueList[i] -= (unsigned long)r->postArgs;
  1230.         }
  1231.         
  1232.         HUnlock(r->postNames);
  1233.         HUnlock(r->postValues);
  1234.     }
  1235.     
  1236.     // Editor's Note: Similarly to above in the HTTPLockParams routine, there's some
  1237.     // fancy and non-obvious pointer manipulation going on here. See explanation above.
  1238.     r->pathArgs -= (unsigned long)*r->storage;
  1239.     r->username -= (unsigned long)*r->storage;
  1240.     r->password -= (unsigned long)*r->storage;
  1241.     r->fromUser -= (unsigned long)*r->storage;
  1242.     r->clientAddress -= (unsigned long)*r->storage;
  1243.     r->serverName -= (unsigned long)*r->storage;
  1244.     r->serverPort -= (unsigned long)*r->storage;
  1245.     r->scriptName -= (unsigned long)*r->storage;
  1246.     r->contentType -= (unsigned long)*r->storage;
  1247.     r->referer -= (unsigned long)*r->storage;
  1248.     r->userAgent -= (unsigned long)*r->storage;
  1249.     r->action -= (unsigned long)*r->storage;
  1250.     r->actionPath -= (unsigned long)*r->storage;
  1251.     r->method -= (unsigned long)*r->storage;
  1252.     r->clientIP -= (unsigned long)*r->storage;
  1253.     r->fullRequest -= (unsigned long)*r->storage;
  1254.     r->connectionID -= (unsigned long)*r->storage;
  1255.  
  1256.     r->searchArgs -= (unsigned long)*r->storage;
  1257.     r->postArgs -= (unsigned long)*r->storage;
  1258.     
  1259.     HUnlock(r->storage);
  1260.  
  1261.     return;
  1262. }
  1263.  
  1264. // [3]    Get a pointer to one of the parameter strings.
  1265. //         This leaves 'r' locked.
  1266.  
  1267. const char *HTTPGetParam(WWWRequest r, WWWParameter par)
  1268. {
  1269.     const char **p;
  1270.     
  1271.     if (par < p_path_args || par > p_connection_id) return "";
  1272.     
  1273.     if (!r->isLocked) HTTPLockParams(r);
  1274.  
  1275.     p = &r->pathArgs + par;
  1276.  
  1277.     return (*p);
  1278. }
  1279.  
  1280. // [4] Get the integer value of a parameter. Result is returned
  1281. //         in 'i'. Function returns 'false' if the parameter is
  1282. //         not an integer.
  1283.  
  1284. Boolean HTTPGetLong(WWWRequest r, WWWParameter par, long *i)
  1285. {
  1286.     const char *p;
  1287.     Boolean wasntLocked = !r->isLocked;
  1288.     Boolean isValid = false;
  1289.     
  1290.     if (par < p_path_args || par > p_connection_id) return 0;
  1291.     
  1292.     *i = 0;
  1293.  
  1294.     p = HTTPGetParam(r, par);
  1295.     
  1296.     if (strlen(p) > 0) {
  1297.         char *q = (char *) p;
  1298.  
  1299.         while (*q == ' ') q++;
  1300.  
  1301.         if (*q == '+' || *q == '-') q++;
  1302.         
  1303.         while (*q != 0) if (!isdigit(*q++)) goto Done;
  1304.  
  1305.         sscanf(p, "%ld", i);
  1306.         
  1307.         isValid = true;
  1308.     }
  1309.  
  1310. Done:
  1311.     if (wasntLocked) HTTPUnlockParams(r);
  1312.  
  1313.     return (isValid);
  1314. }
  1315.  
  1316. // [5]    Copy parameter text into character variable 'result'.
  1317. //        Length of your character variable is in 'len', the
  1318. //         actual length of the parameter is returned in
  1319. //         'actualLen'. Function returns 'false' if the
  1320. //         parameter identifier 'par' is invalid.
  1321.  
  1322. Boolean HTTPCopyParam(WWWRequest r, WWWParameter par, char *result, long len, long *actualLen)
  1323. {
  1324.     const char *p;
  1325.     Boolean wasntLocked = !r->isLocked;
  1326.     
  1327.     if (par < p_path_args || par > p_connection_id) {
  1328.         result[0] = 0;
  1329.         *actualLen = 0;
  1330.         return (false);
  1331.     }
  1332.  
  1333.     p = HTTPGetParam(r, par);
  1334.  
  1335.     *actualLen = strlen(p);
  1336.     strncpy(result, p, len);
  1337.     
  1338.     if (wasntLocked) HTTPUnlockParams(r);
  1339.  
  1340.     return (true);
  1341. }
  1342.  
  1343. // [6/7]    Get number of SEARCH/POST arguments.
  1344.  
  1345. long HTTPGetNumSrchArgs(WWWRequest r)
  1346. {
  1347.     return r->searchNum;
  1348. }
  1349.  
  1350. long HTTPGetNumPostArgs(WWWRequest r)
  1351. {
  1352.     return r->postNum;
  1353. }
  1354.  
  1355. // [8/9]    Get a SEARCH/POST argument by absolute position. 'index' is between
  1356. //          1 and the total number of SEARCH/POST arguments. 'name' receives the
  1357. //            name of the argument at position 'index' and 'value' receives the value.
  1358. //            The lengths of the character arrays 'name' and 'value' are in 'nameLen'
  1359. //            and 'valueLen'. The actual lengths of the items are returned in
  1360. //            'actualNameLen' and 'actualValueLen'. Function returns 'false' if 'index'
  1361. //            is out of range.
  1362.  
  1363. Boolean HTTPGetSrchArgAt(WWWRequest r, long index, char *name, long nameLen, long *actualNameLen, char *value, long valueLen, long *actualValueLen)
  1364. {
  1365.     const char *p;
  1366.     long n = r->searchNum;
  1367.     char **nameList;
  1368.     char **valueList;
  1369.     Boolean wasntLocked = !r->isLocked;
  1370.     
  1371.     // Any arguments to look at?
  1372.  
  1373.     if (n < 1 || index < 1 || index > n) {
  1374.         name[0] = 0;
  1375.         *actualNameLen = 0;
  1376.  
  1377.         value[0] = 0;
  1378.         *actualValueLen = 0;
  1379.  
  1380.         return (false);
  1381.     }
  1382.     
  1383.     // Get requested item...
  1384.  
  1385.     if (wasntLocked) HTTPLockParams(r);
  1386.  
  1387.     nameList = ((char **) *(r->searchNames));
  1388.     valueList = ((char **) *(r->searchValues));
  1389.  
  1390.     p = nameList[index - 1];
  1391.     *actualNameLen = strlen(p);
  1392.     strncpy(name, p, nameLen);
  1393.     
  1394.     p = valueList[index - 1];
  1395.     *actualValueLen = strlen(p);
  1396.     strncpy(value, p, valueLen);
  1397.     
  1398.     if (wasntLocked) HTTPUnlockParams(r);
  1399.  
  1400.     return (true);
  1401. }
  1402.  
  1403. Boolean HTTPGetPostArgAt(WWWRequest r, long index, char *name, long nameLen, long *actualNameLen, char *value, long valueLen, long *actualValueLen)
  1404. {
  1405.     const char *p;
  1406.     long n = r->postNum;
  1407.     char **nameList;
  1408.     char **valueList;
  1409.     Boolean wasntLocked = !r->isLocked;
  1410.     
  1411.     // Any arguments to look at?
  1412.  
  1413.     if (n < 1 || index < 1 || index > n) {
  1414.         name[0] = 0;
  1415.         *actualNameLen = 0;
  1416.  
  1417.         value[0] = 0;
  1418.         *actualValueLen = 0;
  1419.  
  1420.         return (false);
  1421.     }
  1422.     
  1423.     // Get requested item...
  1424.  
  1425.     if (wasntLocked) HTTPLockParams(r);
  1426.  
  1427.     nameList = ((char **) *(r->postNames));
  1428.     valueList = ((char **) *(r->postValues));
  1429.  
  1430.     p = nameList[index - 1];
  1431.     *actualNameLen = strlen(p);
  1432.     strncpy(name, p, nameLen);
  1433.     
  1434.     p = valueList[index - 1];
  1435.     *actualValueLen = strlen(p);
  1436.     strncpy(value, p, valueLen);
  1437.     
  1438.     if (wasntLocked) HTTPUnlockParams(r);
  1439.  
  1440.     return (true);
  1441. }
  1442.  
  1443. // [10/11]    Get number of SEARCH/POST arguments that have the same field
  1444. //            name 'name'. Number is returned in 'numValues'. Function return
  1445. //            'false' if there is no SEARCH/POST argument called 'name'.
  1446.  
  1447. Boolean HTTPGetSrchArgCount(WWWRequest r, char *name, long *numValues)
  1448. {
  1449.     long i;
  1450.     long n = r->searchNum;
  1451.     char **nameList;
  1452.     Boolean wasntLocked = !r->isLocked;    
  1453.     
  1454.     *numValues = 0;
  1455.  
  1456.     // Any arguments to look at?
  1457.  
  1458.     if (n < 1) return (false);
  1459.     
  1460.     // Scan to count the number of times the name appears...
  1461.  
  1462.     if (wasntLocked) HTTPLockParams(r);
  1463.  
  1464.     nameList = ((char **) *(r->searchNames));
  1465.  
  1466.     for (i = 0; i < n; i++) {
  1467.         if (strcmp(name, nameList[i]) == 0) {
  1468.             (*numValues)++;
  1469.         }
  1470.     }
  1471.     
  1472.     if (wasntLocked) HTTPUnlockParams(r);
  1473.  
  1474.     if (*numValues >= 1)
  1475.         return (true);
  1476.     else
  1477.         return (false);
  1478. }
  1479.  
  1480. Boolean HTTPGetPostArgCount(WWWRequest r, char *name, long *numValues)
  1481. {
  1482.     long i;
  1483.     long n = r->postNum;
  1484.     char **nameList;
  1485.     Boolean wasntLocked = !r->isLocked;    
  1486.     
  1487.     *numValues = 0;
  1488.  
  1489.     // Any arguments to look at?
  1490.  
  1491.     if (n < 1) return (false);
  1492.     
  1493.     // Scan to count the number of times the name appears...
  1494.  
  1495.     if (wasntLocked) HTTPLockParams(r);
  1496.  
  1497.     nameList = ((char **) *(r->postNames));
  1498.  
  1499.     for (i = 0; i < n; i++) {
  1500.         if (strcmp(name, nameList[i]) == 0) {
  1501.             (*numValues)++;
  1502.         }
  1503.     }
  1504.     
  1505.     if (wasntLocked) HTTPUnlockParams(r);
  1506.  
  1507.     if (*numValues >= 1)
  1508.         return (true);
  1509.     else
  1510.         return (false);
  1511. }
  1512.  
  1513. // [12/13]    Try to get instance 'index' of a multi-valued SEARCH/POST argument.
  1514. //            Function returns empty string if 'index' is out of range or if 'name' doesn't
  1515. //            exist. Function leaves 'r' locked on exit.
  1516.  
  1517. const char *HTTPGetMultipleSrchArg(WWWRequest r, char *name, long index)
  1518. {
  1519.     long i, count = 0;
  1520.     long n = r->searchNum;
  1521.     char **nameList;
  1522.     char **valueList;
  1523.     Boolean wasntLocked = !r->isLocked;    
  1524.     
  1525.     // Any arguments to look at?
  1526.  
  1527.     if (n < 1 || index < 1 || index > n) return "";
  1528.     
  1529.     // Scan to locate requested item...
  1530.  
  1531.     if (wasntLocked) HTTPLockParams(r);
  1532.  
  1533.     nameList = ((char **) *(r->searchNames));
  1534.     valueList = ((char **) *(r->searchValues));
  1535.  
  1536.     for (i = 0; i < n; i++) {
  1537.         if (strcmp(name, nameList[i]) == 0) {
  1538.             count++;
  1539.             if (count == index) {
  1540.                 return (valueList[i]);
  1541.             }
  1542.         }
  1543.     }
  1544.     
  1545.     return "";
  1546. }
  1547.  
  1548. const char *HTTPGetMultiplePostArg(WWWRequest r, char *name, long index)
  1549. {
  1550.     long i, count = 0;
  1551.     long n = r->postNum;
  1552.     char **nameList;
  1553.     char **valueList;
  1554.     Boolean wasntLocked = !r->isLocked;    
  1555.     
  1556.     // Any arguments to look at?
  1557.  
  1558.     if (n < 1 || index < 1 || index > n) return "";
  1559.     
  1560.     // Scan to locate requested item...
  1561.  
  1562.     if (wasntLocked) HTTPLockParams(r);
  1563.  
  1564.     nameList = ((char **) *(r->postNames));
  1565.     valueList = ((char **) *(r->postValues));
  1566.  
  1567.     for (i = 0; i < n; i++) {
  1568.         if (strcmp(name, nameList[i]) == 0) {
  1569.             count++;
  1570.             if (count == index) {
  1571.                 return (valueList[i]);
  1572.             }
  1573.         }
  1574.     }
  1575.     
  1576.     return "";
  1577. }
  1578.  
  1579. // [14/15]    Get integer value of instance 'index' of a multi-valued SEARCH/POST
  1580. //            argument called  'name'. Function returns value in 'i'. Function returns
  1581. //            'false' if 'index' is out of range or the argument is not an integer.
  1582.  
  1583. Boolean HTTPGetLongMultipleSrchArg(WWWRequest r, char *name, long index, long *i)
  1584. {
  1585.     const char *p;
  1586.     Boolean wasntLocked = !r->isLocked;
  1587.     Boolean isValid = false;
  1588.  
  1589.     *i = 0;
  1590.  
  1591.     p = HTTPGetMultipleSrchArg(r, name, index);
  1592.     
  1593.     if (strlen(p) > 0) {
  1594.         char *q = (char *) p;
  1595.  
  1596.         while (*q == ' ') q++;
  1597.  
  1598.         if (*q == '+' || *q == '-') q++;
  1599.         
  1600.         while (*q != 0) if (!isdigit(*q++)) goto Done;
  1601.  
  1602.         sscanf(p, "%ld", i);
  1603.         
  1604.         isValid = true;
  1605.     }
  1606.  
  1607. Done:
  1608.     if (wasntLocked) HTTPUnlockParams(r);
  1609.  
  1610.     return (isValid);
  1611. }
  1612.  
  1613. Boolean HTTPGetLongMultiplePostArg(WWWRequest r, char *name, long index, long *i)
  1614. {
  1615.     const char *p;
  1616.     Boolean wasntLocked = !r->isLocked;
  1617.     Boolean isValid = false;
  1618.  
  1619.     *i = 0;
  1620.  
  1621.     p = HTTPGetMultiplePostArg(r, name, index);
  1622.     
  1623.     if (strlen(p) > 0) {
  1624.         char *q = (char *) p;
  1625.  
  1626.         while (*q == ' ') q++;
  1627.  
  1628.         if (*q == '+' || *q == '-') q++;
  1629.         
  1630.         while (*q != 0) if (!isdigit(*q++)) goto Done;
  1631.  
  1632.         sscanf(p, "%ld", i);
  1633.         
  1634.         isValid = true;
  1635.     }
  1636.  
  1637. Done:
  1638.     if (wasntLocked) HTTPUnlockParams(r);
  1639.  
  1640.     return (isValid);
  1641. }
  1642.  
  1643. // [16/17]    Copy the contents of instance 'index' of a multi-valued SEARCH/POST
  1644. //            argument called 'name'. Function returns text in 'value'. Length of 'value'
  1645. //            string is in 'len', actual length of the value string is returned in 'actualLen'
  1646. //             Function returns 'false' if 'index' is out of range or 'name' does not exist.
  1647.  
  1648. Boolean HTTPCopyMultipleSrchArg(WWWRequest r, char *name, long index, char *value, long len, long *actualLen)
  1649. {
  1650.     long i, count = 0;
  1651.     const char *p;
  1652.     long n = r->searchNum;
  1653.     char **nameList;
  1654.     char **valueList;
  1655.     Boolean wasntLocked = !r->isLocked;    
  1656.     
  1657.     // Any arguments to look at?
  1658.  
  1659.     if (n < 1 || index < 1 || index > n) {
  1660.         value[0] = 0;
  1661.         *actualLen = 0;
  1662.         return (false);
  1663.     }
  1664.     
  1665.     // Scan to locate requested item...
  1666.  
  1667.     if (wasntLocked) HTTPLockParams(r);
  1668.  
  1669.     nameList = ((char **) *(r->searchNames));
  1670.     valueList = ((char **) *(r->searchValues));
  1671.  
  1672.     for (i = 0; i < n; i++) {
  1673.         if (strcmp(name, nameList[i]) == 0) {
  1674.             count++;
  1675.             if (count == index) {
  1676.                 p = valueList[i];
  1677.                 *actualLen = strlen(p);
  1678.                 strncpy(value, p, len);
  1679.                 if (wasntLocked) HTTPUnlockParams(r);
  1680.                 return (true);
  1681.             }
  1682.         }
  1683.     }
  1684.     
  1685.     value[0] = 0;
  1686.     *actualLen = 0;
  1687.     
  1688.     if (wasntLocked) HTTPUnlockParams(r);
  1689.     
  1690.     return (false);
  1691. }
  1692.  
  1693. Boolean HTTPCopyMultiplePostArg(WWWRequest r, char *name, long index, char *value, long len, long *actualLen)
  1694. {
  1695.     long i, count = 0;
  1696.     const char *p;
  1697.     long n = r->postNum;
  1698.     char **nameList;
  1699.     char **valueList;
  1700.     Boolean wasntLocked = !r->isLocked;    
  1701.     
  1702.     // Any arguments to look at?
  1703.  
  1704.     if (n < 1 || index < 1 || index > n) {
  1705.         value[0] = 0;
  1706.         *actualLen = 0;
  1707.         return (false);
  1708.     }
  1709.     
  1710.     // Scan to locate requested item...
  1711.  
  1712.     if (wasntLocked) HTTPLockParams(r);
  1713.  
  1714.     nameList = ((char **) *(r->postNames));
  1715.     valueList = ((char **) *(r->postValues));
  1716.  
  1717.     for (i = 0; i < n; i++) {
  1718.         if (strcmp(name, nameList[i]) == 0) {
  1719.             count++;
  1720.             if (count == index) {
  1721.                 p = valueList[i];
  1722.                 *actualLen = strlen(p);
  1723.                 strncpy(value, p, len);
  1724.                 if (wasntLocked) HTTPUnlockParams(r);
  1725.                 return (true);
  1726.             }
  1727.         }
  1728.     }
  1729.     
  1730.     value[0] = 0;
  1731.     *actualLen = 0;
  1732.     
  1733.     if (wasntLocked) HTTPUnlockParams(r);
  1734.     
  1735.     return (false);
  1736. }
  1737.  
  1738. // -----------------------------------------------------
  1739.  
  1740. // [1]    Get the handle that holds the HTML response page.
  1741.  
  1742. Handle HTMLGetResponseHandle(WWWRequest r)
  1743. {
  1744.     return (r->response);
  1745. }
  1746.  
  1747. // [2]    Clear out the current response page (except for the
  1748. //        HTTP header, and start over.
  1749.  
  1750. OSErr HTMLClearPage(Handle r)
  1751. {
  1752.     SetHandleSize(r, gHTTPHeaderLen);
  1753.     
  1754.     return (MemError());
  1755. }
  1756.  
  1757. // [3]    Append contents of handle 'h' to response page.
  1758.  
  1759. OSErr HTMLAppendHandle(Handle r, Handle h)
  1760. {
  1761.     if (h != nil && *h != nil && GetHandleSize(h) > 0)
  1762.         return (HandAndHand(h, r));
  1763.     else
  1764.         return (noErr);
  1765. }
  1766.  
  1767. // [4]    Append TEXT resource (ID=iTEXTResID) to response page.
  1768.  
  1769. OSErr HTMLAppendTEXT(Handle r, long iTEXTResID)
  1770. {
  1771.     Handle h = Get1Resource('TEXT', iTEXTResID);
  1772.     
  1773.     OSErr err = ResError();
  1774.  
  1775.     if (h == nil || err != noErr) {
  1776.         Str31 s;
  1777.         NumToString(iTEXTResID, s);
  1778.         s[s[0] + 1] = 0;
  1779.  
  1780.         ACGILog("Cound not load requested TEXT resource. ID was:");
  1781.         ACGILog((char *) &s[1]);
  1782.  
  1783.         if (err != noErr)
  1784.             return (err);
  1785.         else
  1786.             return (resNotFound);
  1787.     }
  1788.  
  1789.     return (HandAndHand(h, r));
  1790. }
  1791.  
  1792. // [5]    Append STR resource (ID=iSTRResID) to the response page.
  1793.  
  1794. OSErr HTMLAppendString(Handle r, long iSTRResID)
  1795. {
  1796.     StringHandle h = GetString(iSTRResID);
  1797.     
  1798.     OSErr err = ResError();
  1799.  
  1800.     if (h == nil || err != noErr) {
  1801.         Str31 s;
  1802.         NumToString(iSTRResID, s);
  1803.         s[s[0] + 1] = 0;
  1804.  
  1805.         ACGILog("Cound not load requested STR resource. ID was:");
  1806.         ACGILog((char *) &s[1]);
  1807.  
  1808.         if (err != noErr)
  1809.             return (err);
  1810.         else
  1811.             return (resNotFound);
  1812.     }
  1813.  
  1814.     HLockHi((Handle) h);
  1815.     err = PtrAndHand(*h + 1, r, **h);
  1816.     HUnlock((Handle) h);
  1817.  
  1818.     return (err);
  1819. }
  1820.  
  1821. // [6]    Append string at location 'index' in STR# resource
  1822. //        (ID=iSTRResID) to the response page.
  1823.  
  1824. OSErr HTMLAppendIndString(Handle r, long iSTRResID, long index)
  1825. {
  1826.     Str255 theStr;
  1827.     OSErr err;
  1828.  
  1829.     GetIndString(theStr, iSTRResID, index);
  1830.     
  1831.     if (theStr[0] == 0) {
  1832.         ACGILog("Request to append empty string from STR# resource. Resource number:");
  1833.  
  1834.         NumToString(iSTRResID, theStr);
  1835.         theStr[theStr[0] + 1] = 0;
  1836.         ACGILog((char *) &theStr[1]);
  1837.  
  1838.         ACGILog("STR# index was:");
  1839.  
  1840.         NumToString(index, theStr);
  1841.         theStr[theStr[0] + 1] = 0;
  1842.         ACGILog((char *) &theStr[1]);
  1843.     }
  1844.  
  1845.     err = PtrAndHand(&theStr[1], r, theStr[0]);
  1846.  
  1847.     return (err);
  1848. }
  1849.  
  1850. // [7]    Append local file to response page.
  1851.  
  1852. OSErr HTMLAppendFile(Handle r, char *localFileName)
  1853. {
  1854.     Size theLen, origLen;
  1855.     char *text = nil;
  1856.     OSErr err;
  1857.     
  1858.     // Open the file in the directory our ACGI is running in.
  1859.  
  1860.     FILE *f = fopen(localFileName, "rb");
  1861.     
  1862.     if (f == NULL) {
  1863.         ACGILog("Could not open HTML file to append to response. File name was:");
  1864.         ACGILog(localFileName);
  1865.         return (fnfErr);
  1866.     }
  1867.     
  1868.     // Determine number of characters in the file.
  1869.     
  1870.     fseek(f, 0L, SEEK_END);
  1871.  
  1872.     theLen = ftell(f);
  1873.  
  1874.     if (theLen <= 0) {
  1875.         ACGILog("Could not determine size of HTML file for response. File name was:");
  1876.         ACGILog(localFileName);
  1877.         return (fnfErr);
  1878.     }
  1879.     
  1880.     // Rewind to the start of the file.
  1881.  
  1882.     fseek(f, 0L, SEEK_SET);
  1883.     
  1884.     // Extend the response handle.
  1885.     
  1886.     origLen = GetHandleSize(r);
  1887.     
  1888.     SetHandleSize(r, origLen + theLen);
  1889.  
  1890.     err = MemError();
  1891.     
  1892.     if (err != noErr) {
  1893.         fclose(f);
  1894.         ACGILog("Ran out of memory appending HTML file to response. File name was:");
  1895.         ACGILog(localFileName);
  1896.         return (err);
  1897.     }
  1898.     
  1899.     // Add the file contents to the response.
  1900.     
  1901.     HLockHi(r);
  1902.     fread(*r + origLen, theLen, 1, f);
  1903.     HUnlock(r);
  1904.  
  1905.     fclose(f);
  1906.     
  1907.     return (noErr);
  1908. }
  1909.  
  1910. // [8]    Append C-string to response page.
  1911.  
  1912. OSErr HTMLAppendCString(Handle r, char *cString)
  1913. {
  1914.     return (PtrAndHand(cString, r, strlen(cString)));
  1915. }
  1916.  
  1917. // [9]    Append Pascal-string to response page.
  1918.  
  1919. OSErr HTMLAppendPString(Handle r, StringPtr pString)
  1920. {
  1921.     return (PtrAndHand(&pString[1], r, pString[0]));
  1922. }
  1923.  
  1924. // [10]    Append text buffer of length 'len' to response page.
  1925.  
  1926. OSErr HTMLAppendBuffer(Handle r, char *buffer, long len)
  1927. {
  1928.     return (PtrAndHand(buffer, r, len));
  1929. }
  1930.